


import java.awt.*;
import javax.swing.*;
import java.awt.event.*;
import javax.swing.event.*;

import java.text.*;
import java.awt.font.*;

import SSF.Net.*;
import SSF.OS.BGP4.*;
import SSF.OS.BGP4.Comm.*;
import SSF.OS.BGP4.Util.*;
import SSF.Util.Streams.*;
import SSF.OS.NetFlow.BytesUtil;

import com.renesys.raceway.views.*;

// Hacked from SSF.OS.BGP4.VerbosePlayer 

/** This Stream.Player implementation provides default 
 *  implementations for the describe() method (using 
 *  VerbosePlayer's code to render records as strings)
 *  and the getControls() method (the big option checkbox
 *  panel).  It will not be part of Raceway in the long 
 *  run; instead, it can be transitioned into the BGP4 
 *  distribution.   That will make it easier to maintain
 *  as BGP4 and its monitoring infrastructure grows. 
 */
public class bgpStreamPlayer implements Stream.Player {

    public void play(Stream.Record rec) {/*noop*/};

    public String toString() {
	return "BGP4 Event Monitor";
    }

    protected Stream stream;

    public void setStream(Stream stream) {
	this.stream = stream;
    }

    public void start(Stream stream) {
	System.err.println("** bgpStreamPlayer starting");
    }
    public void finish(Stream stream) {
	System.err.println("** bgpStreamPlayer finishing");
    }

  /** Indicates whether or not to use NHI addressing when possible. */
  public boolean usenhi = false;

    public AttributedString describe(Stream.Record record) {
	
	int typeid   = record.type_code;
	int srcid    = record.source_code;
	double time  = record.timestamp;
	byte[] buf   = record.bytes;
	int bindex   = record.offset;
	int length   = record.length;
	
    if (!stream.getRecordTypeString(typeid).equals("SSF.OS.BGP4")) {
	// Ah, forget it. 
	return null; // let someone else render it
    }

    Color newColor = new Color(0x33,0x33,0x33);
    
    int w =0,h =0;

    // the NH address of the router
    String nh  = stream.getRecordSourceString(srcid); 
	
    String str = Debug.hdr(nh,time);

    int typ = (int)buf[bindex++];
    int caseno = -1;

    StringBuffer str1 = new StringBuffer("");
    StringBuffer str2 = new StringBuffer("");
    StringBuffer str3 = new StringBuffer("");

    String s = null;

    double d = -1.0;
    int connection_state = -1, peertyp = -1, dop = -1, i = -1, i2 = -1;
    int subtyp = -1;
    boolean permissible = false;

    //System.err.println("BGP.Render "+typ+" "+nh+" "+str);

    if (typ>=0 && typ<options.length && !options[typ]) 
	return Stream.IGNORE; // suppressed.

    if (nhiFilter!=null && !nhiFilter.equals("") && !nhiFilter.equals("-none-")
	&& !stream.getRecordSourceString(srcid).startsWith(nhiFilter))
	return Stream.IGNORE; // suppressed.

    //System.err.println("BGP.Render rendering "+typ+" "+nh+" "+str);
    
    switch (typ) {
    case Monitor.USENHI:
      usenhi = (buf[bindex++] == 1);
      return new AttributedString(str+"BGP:Use NHI");
    case Monitor.START_EVENT:
      caseno = (int)buf[bindex++];
      switch (caseno) {
      case 0:  str += "BGPStart event occurred\n";                break;
      case 1:  str += "BGPStart event occurred in Idle state\n";  break;
      case 2:  str += "BGPStart event ignored\n";  	          break;
      }
      break;
    case Monitor.STOP_EVENT:
      caseno = (int)buf[bindex++];
      switch (caseno) {
      case 0:  str += "BGPStop event occurred\n"; 	                break;
      case 1:  str += "BGPStop event occurred in Established state\n";  break;
      }
      break;
    case Monitor.TRANSOPEN:
      caseno = (int)buf[bindex++];
      connection_state = (int)buf[bindex++];
      switch (caseno) {
      case 0:
        bindex += Monitor.bytes2nh(str1, buf, bindex);
        str += "TransConnOpen event, peer " + str1 + ", " +
               BGPSession.statestr[connection_state] + " state\n";
        break;
      case 1:
        str += "TransConnOpen event in Established state\n";
        break;
      }
      break;
    case Monitor.CPU_BUSY:
      newColor = Color.blue;
      caseno = (int)buf[bindex++];
      if (caseno == 0) {
        str += "CPU activity stops\n";
      } else if (caseno == 1) {
        str += "CPU activity starts\n";
      } else {
        Debug.gerr("unexpected case in CPU_BUSY: " + caseno);
      }
      break;
    case Monitor.TRANSCLOSE:
      str += "TransConnClose event occurred\n";
      break;
    case Monitor.TRANSFAIL:
      caseno = (int)buf[bindex++];
      switch (caseno) {
      case 0: str += "TransConnOpenFail event occurred\n";  break;
      case 1: str += "TransConnOpenFail event occurred in Established state\n";
 	break;
      }
      break;
    case Monitor.TRANSFATAL:
      str += "TransFatalError event occurred\n";
      break;
    case Monitor.SND_OPEN:
      bindex += Monitor.bytes2nh(str1, buf, bindex);
      str += "snd Open to bgp@" + str1 + "\n";
      break;
    case Monitor.RCV_OPEN:
      connection_state = (int)buf[bindex++];
      bindex += Monitor.bytes2nh(str1, buf, bindex);
      str += "rcv Open frm bgp@" + str1 + " while in " +
             BGPSession.statestr[connection_state] + " state\n";
      break;
    case Monitor.RCV_UPDATE:
      peertyp = (int)buf[bindex++];
      bindex += Monitor.bytes2nh(str1, buf, bindex);
      bindex += Monitor.bytes2update(str2, buf, bindex, usenhi);

      if (peertyp == 0) { // external update
        str += "rcv update frm bgp@" + str1;
      } else { // internal update
        str += "rcv [internal] update frm bgp@" + str1;
      }
      str += " " + str2 + "\n";
      break;
    case Monitor.SND_UPDATE:
      caseno = (int)buf[bindex++];
      bindex += Monitor.bytes2nh(str1, buf, bindex);
      bindex += Monitor.bytes2update(str2, buf, bindex, usenhi);
      s = "";
      switch (caseno) {
      case 0: // no withdrawal, wasn't waiting
        s = "snd update to bgp@";
 	break;
      case 1: // this update was waiting (in waiting_adv)
        s = "snd update (waiting) to bgp@";
 	break;
      }
      str += s + str1 + " " + str2 + "\n";
      break;
    case Monitor.RCV_KA:
      bindex += Monitor.bytes2nh(str1, buf, bindex);
      str += "rcv keepalive from bgp@" + str1 + "\n";
      break;
    case Monitor.SND_KA:
      bindex += Monitor.bytes2nh(str1, buf, bindex);
      str += "snd keepalive to bgp@" + str1 + "\n";
      break;
    case Monitor.RCV_NOTIF:
      bindex += Monitor.bytes2nh(str1, buf, bindex);
      str += "rcv notification from bgp@" + str1 + "\n";
      break;
    case Monitor.SND_NOTIF:
      bindex += Monitor.bytes2nh(str1, buf, bindex);
      str += "snd notification to bgp@" + str1 + "\n";
      break;
    case Monitor.SET_KA:
      bindex += Monitor.bytes2nh(str1, buf, bindex);
      str += "rst keepalive timer for bgp@" + str1 + "\n";
      break;
    case Monitor.SET_HOLD:
      bindex += Monitor.bytes2nh(str1, buf, bindex);
      str += "rst hold timer for bgp@" + str1 + "\n";
      break;
    case Monitor.SET_MRAI:
      bindex += Monitor.bytes2nh(str1, buf, bindex);
      str += "set mrai timer for bgp@" + str1 + "\n";
      break;
    case Monitor.KA_EXP:
      bindex += Monitor.bytes2nh(str1, buf, bindex);
      str += "exp keepalive timer for bgp@" + str1 + "\n";
      break;
    case Monitor.CONNRETRY_EXP:
      bindex += Monitor.bytes2nh(str1, buf, bindex);
      str += "exp connect retry timer for bgp@" + str1 + "\n";
      break;
    case Monitor.HOLD_EXP:
      bindex += Monitor.bytes2nh(str1, buf, bindex);
      str += "exp hold timer for bgp@" + str1 + "\n";
      break;
    case Monitor.MRAI_EXP:
      bindex += Monitor.bytes2nh(str1, buf, bindex);
      bindex += Monitor.bytes2ipprefix(str2, buf, bindex, usenhi);
      str += "exp mrai timer for bgp@" + str1 + " (dst=" + str2 + ")\n";
      break;
    case Monitor.HANDLE_UPDATE:
      caseno = (int)buf[bindex++];
      switch (caseno) {
      case 0:
        peertyp = (int)buf[bindex++]; // internal or external update
        bindex += Monitor.bytes2nh(str1, buf, bindex);
        bindex += Monitor.bytes2update(str2, buf, bindex, usenhi);

        if (peertyp == 0) { // external update
          str += "rcv update frm bgp@" + str1;
        } else { // internal update
          str += "rcv [internal] update frm bgp@" + str1;
        }
        str += " " + str2 + "\n";
        break;
      case 1:
        bindex += Monitor.bytes2ipprefix(str1, buf, bindex, usenhi);
        bindex += Monitor.bytes2cl(str2, buf, bindex);
        bindex += Monitor.bytes2nh(str3, buf, bindex);
        str += " ... ignoring rte=" + str1 + ",clu=" + str2 + " from bgp@" +
               str3 + " (has cluster loop)\n";
        break;
      case 2:
        bindex += Monitor.bytes2ipprefix(str1, buf, bindex, usenhi);
        bindex += Monitor.bytes2aspath(str2, buf, bindex, usenhi);
        bindex += Monitor.bytes2nh(str3, buf, bindex);
        str += " ... ignoring rte=" + str1 + ",asp=" + str2 +
               " from bgp@" + str3 + " (has loop)\n";
        break;
      case 3:
        bindex += Monitor.bytes2ipprefix(str1, buf, bindex, usenhi);
        bindex += Monitor.bytes2aspath(str2, buf, bindex, usenhi);
        bindex += Monitor.bytes2nh(str3, buf, bindex);
        str += " ... withdrawing rte=" + str1 + ",asp=" + str2 +
               " from Adj-RIB-In for bgp@" + str3 + "\n";
        break;
      case 4:
        bindex += Monitor.bytes2ipprefix(str1, buf, bindex, usenhi);
        bindex += Monitor.bytes2nh(str2, buf, bindex);
        str += " ... no route with nlri=" + str1 +
               " existed in Adj-RIB-In for bgp@" + str2 + "\n";
        break;
      case 5:
        bindex += Monitor.bytes2ipprefix(str1, buf, bindex, usenhi);
        bindex += Monitor.bytes2nh(str2, buf, bindex);
        str += " ... removed rte=" + str1 +
               " from Adj-RIB-In for bgp@" + str2 + "\n";
        break;
      case 6:
        bindex += Monitor.bytes2ipprefix(str1, buf, bindex, usenhi);
        bindex += Monitor.bytes2nh(str2, buf, bindex);
        str += " ... added rte=" + str1 +
               " to Adj-RIB-In for bgp@" + str2 + "\n";
        break;
      }
      break;
    case Monitor.ADDED_ROUTE:
      boolean isself = false;
      caseno = (int)buf[bindex++];
      switch (caseno) {
      case 0:
        bindex += Monitor.bytes2ipprefix(str1, buf, bindex, usenhi);
        bindex += Monitor.bytes2nh(str2, buf, bindex);
 	str += "adding " + str1 + " to Adj-RIB-In for bgp@" + str2 + "\n";
        break;
      case 1:
        isself = (buf[bindex++] == 1);
        if (!isself) {
          bindex += Monitor.bytes2ipprefix(str1, buf, bindex, usenhi);
        }
        bindex += Monitor.bytes2ipprefix(str2, buf, bindex, usenhi);
        dop = (int)buf[bindex++];
        if (isself) {
          s = "self";
        } else {
          s = str1.toString();
        }
 	str += "adding rte=" + str2 + ",nxt=" + s + ",dop=" + dop +
               " to Loc-RIB\n";
        break;
      case 2:
        isself = (buf[bindex++] == 1);
        if (!isself) {
          bindex += Monitor.bytes2ipprefix(str1, buf, bindex, usenhi);
        }
        bindex += Monitor.bytes2ipprefix(str2, buf, bindex, usenhi);
        bindex += Monitor.bytes2nh(str3, buf, bindex);
        if (isself) {
          s = "self";
        } else {
          s = str1.toString();
        }
 	str += "adding rte=" + str2 + " nxt=" + s +
               " to Adj-RIB-Out for bgp@" + str3 + "\n";
        break;
      }
      break;
    case Monitor.DOP_CALC:
      caseno = (int)buf[bindex++];
      bindex += Monitor.bytes2ipprefix(str1, buf, bindex, usenhi);
      dop = (int)buf[bindex++];
      switch (caseno) {
      case 0:
        str += "degree of pref for rte=" + str1 + " is " + dop +
               " (used local pref)\n";
        break;
      case 1:
        str += "degree of pref for rte=" + str1 + " is " + dop +
               " (based on hopcount)\n";
        break;
      case 2:
        str += "degree of pref for rte=" + str1 + " is " + dop +
               " (was missing AS path)\n";
        break;
      case 3:
        str += "degree of pref for rte=" + str1 + " is " + dop +
               " (local AS prefix)\n";
        break;
      }
      break;
    case Monitor.DEC_PROC:
      caseno = (int)buf[bindex++];
      switch (caseno) { // which phase of the Decision Process
      case 1: // Phase 1
        i = (int)buf[bindex++];
        switch (i) {
        case 0:  str += "Decision Process Phase 1\n";
          break;
        case 1:
          permissible = (buf[bindex++] == 1);
          bindex += Monitor.bytes2ipprefix(str1, buf, bindex, usenhi);
          bindex += Monitor.bytes2aspath(str2, buf, bindex, usenhi);
          bindex += Monitor.bytes2nh(str3, buf, bindex);
          str += " ... inbound policy " + (permissible?"permitted":"denied") +
                 " rte=" + str1 + ",asp=" + str2 + " from bgp@" + str3 + "\n";
          break;
        case 2:
          bindex += Monitor.bytes2ipprefix(str1, buf, bindex, usenhi);
          dop = (int)buf[bindex++];
          str += " ... degree of pref for rte=" + str1 + " is " + dop + "\n";
          break;
        }
        break;
      case 2: // Phase 2
        i = (int)buf[bindex++];
        switch (i) {
        case 0:  str += "Decision Process Phase 2\n";
          break;
        case 1:
          bindex += Monitor.bytes2ipprefix(str1, buf, bindex, usenhi);
          bindex += Monitor.bytes2aspath(str2, buf, bindex, usenhi);
          str += " ... removed rte=" + str1 + ",asp=" + str2 +
                 " from Loc-RIB\n";
          break;
        case 2:
          bindex += Monitor.bytes2ipprefix(str1, buf, bindex, usenhi);
          bindex += Monitor.bytes2aspath(str2, buf, bindex, usenhi);
          str += " ... added rte=" + str1 + ",asp=" + str2 +
                 " to Loc-RIB\n";
          break;
        case 3:
          bindex += Monitor.bytes2ipprefix(str1, buf, bindex, usenhi);
          bindex += Monitor.bytes2nh(str2, buf, bindex);
          str += " ... ignoring withdrawal for nlri=" + str1 + " from bgp@" +
                 str2 + " (not in Loc-RIB)\n";
          break;
        case 4:
          bindex += Monitor.bytes2ipprefix(str1, buf, bindex, usenhi);
          bindex += Monitor.bytes2aspath(str2, buf, bindex, usenhi);
          bindex += Monitor.bytes2nh(str3, buf, bindex);
          str += " ... ignoring feasible rte=" + str1 + ",asp=" + str2 +
                 " from bgp@" + str3 + " (not permissible)\n";
          break;
        case 5:
          bindex += Monitor.bytes2ipprefix(str1, buf, bindex, usenhi);
          bindex += Monitor.bytes2aspath(str2, buf, bindex, usenhi);
          bindex += Monitor.bytes2nh(str3, buf, bindex);
          str += " ... not choosing feasible rte=" + str1 + ",asp=" +
                 str2 + " from bgp@" + str3 + " (not better than current)\n";
          break;
        }
        break;
      case 3: // Phase 3
        i = (int)buf[bindex++];
        switch (i) {
        case 0:  str += "Decision Process Phase 3\n";
          break;
        case 1:
          bindex += Monitor.bytes2ipprefix(str1, buf, bindex, usenhi);
          bindex += Monitor.bytes2aspath(str2, buf, bindex, usenhi);
          bindex += Monitor.bytes2nh(str3, buf, bindex);
          str += " ... removed rte=" + str1 + ",asp=" + str2 +
                 " from Adj-RIB-Out for bgp@" + str3 + "\n";
          break;
        case 2:
          bindex += Monitor.bytes2ipprefix(str1, buf, bindex, usenhi);
          bindex += Monitor.bytes2aspath(str2, buf, bindex, usenhi);
          bindex += Monitor.bytes2nh(str3, buf, bindex);
          str += " ... added rte=" + str1 + ",asp=" + str2 +
                 " to Adj-RIB-Out for bgp@" + str3 + "\n";
          break;
        case 3:
          bindex += Monitor.bytes2ipprefix(str1, buf, bindex, usenhi);
          bindex += Monitor.bytes2aspath(str2, buf, bindex, usenhi);
          bindex += Monitor.bytes2nh(str3, buf, bindex);
          str += " ... not adding rte=" + str1 + ",asp=" + str2 +
                 " to Adj-RIB-Out for bgp@" + str3 + " (was sender)\n";
          break;
        case 4:
          bindex += Monitor.bytes2ipprefix(str1, buf, bindex, usenhi);
          bindex += Monitor.bytes2aspath(str2, buf, bindex, usenhi);
          bindex += Monitor.bytes2nh(str3, buf, bindex);
          str += " ... not adding rte=" + str1 + ",asp=" + str2 +
                 " to Adj-RIB-Out for bgp@" + str3 + " (filtered)\n";
          break;
        case 5:
          bindex += Monitor.bytes2ipprefix(str1, buf, bindex, usenhi);
          bindex += Monitor.bytes2aspath(str2, buf, bindex, usenhi);
          bindex += Monitor.bytes2nh(str3, buf, bindex);
          str += " ... not adding rte=" + str1 + ",asp=" + str2 +
                 " to Adj-RIB-Out for bgp@" + str3 + " (was internal " +
                 "originator)\n";
          break;
        case 6:
          bindex += Monitor.bytes2ipprefix(str1, buf, bindex, usenhi);
          bindex += Monitor.bytes2aspath(str2, buf, bindex, usenhi);
          bindex += Monitor.bytes2nh(str3, buf, bindex);
          str += " ... not adding rte=" + str1 + ",asp=" + str2 +
                 " to Adj-RIB-Out for bgp@" + str3 + " (from & to " +
                 "non-client)\n";
          break;
        case 7:
          bindex += Monitor.bytes2ipprefix(str1, buf, bindex, usenhi);
          bindex += Monitor.bytes2aspath(str2, buf, bindex, usenhi);
          bindex += Monitor.bytes2nh(str3, buf, bindex);
          str += " ... not adding rte=" + str1 + ",asp=" + str2 +
                 " to Adj-RIB-Out for bgp@" + str3 + " (not a reflector)\n";
          break;
        case 8:
          bindex += Monitor.bytes2ipprefix(str1, buf, bindex, usenhi);
          bindex += Monitor.bytes2aspath(str2, buf, bindex, usenhi);
          bindex += Monitor.bytes2nh(str3, buf, bindex);
          str += " ... not adding rte=" + str1 + ",asp=" + str2 +
                 " to Adj-RIB-Out for bgp@" + str3 + " (would be loop)\n";
          break;
        }
        break;
      }
      break;
    case Monitor.EXT_UPDATE:
      caseno = (int)buf[bindex++];
      switch (caseno) {
      case 0:  str += "External Update\n";  break;
      case 1:
        bindex += Monitor.bytes2nh(str1, buf, bindex);
        str += " ... can't do external update for bgp@" + str1 +
               " (no peering session exists)\n";
        break;
      case 2:
        bindex += Monitor.bytes2ipprefix(str1, buf, bindex, usenhi);
        bindex += Monitor.bytes2aspath(str2, buf, bindex, usenhi);
        bindex += Monitor.bytes2nh(str3, buf, bindex);
        str += " ... added rte=" + str1 + ",asp=" + str2 +
               " to wait list for bgp@" + str3 + "\n";
        break;
      case 3:
        bindex += Monitor.bytes2nh(str1, buf, bindex);
        bindex += Monitor.bytes2update(str2, buf, bindex, usenhi);
        str += " ... snd update [after possible CPU delay] to bgp@" + str1 +
               " " + str2 + "\n";
        break;
      case 4:
        bindex += Monitor.bytes2nh(str1, buf, bindex);
        bindex += Monitor.bytes2update(str2, buf, bindex, usenhi);
        str += " ... snd update (waiting) [after possible CPU delay] to bgp@"
               + str1 + " " + str2 + "\n";
        break;
      case 5:
        bindex += Monitor.bytes2ipprefix(str1, buf, bindex, usenhi);
        bindex += Monitor.bytes2nh(str2, buf, bindex);
        str += " ... removing redundant withdrawal nlri=" + str1 +
               " from update before sending to " + str2 + "\n";
        break;
      case 6:
        bindex += Monitor.bytes2ipprefix(str1, buf, bindex, usenhi);
        bindex += Monitor.bytes2aspath(str2, buf, bindex, usenhi);
        bindex += Monitor.bytes2nh(str3, buf, bindex);
        str += " ... removed rte=" + str1 + ",asp=" + str2 +
               " from wait list for bgp@" + str3 + "\n";
        break;
      case 7:
        bindex += Monitor.bytes2ipprefix(str1, buf, bindex, usenhi);
        bindex += Monitor.bytes2nh(str2, buf, bindex);
        str += " ... removed wd=" + str1 + " from wd wait list for bgp@" +
               str2 + "\n";
        break;
      case 8:
        bindex += Monitor.bytes2ipprefix(str1, buf, bindex, usenhi);
        bindex += Monitor.bytes2nh(str2, buf, bindex);
        str += " ... added wd=" + str1 + " to wd wait list for bgp@" +
               str2 + "\n";
        break;
      }
      break;
    case Monitor.ID_DATA:
      bindex += Monitor.bytes2bgpid(str1, buf, bindex);
      int asnum = BytesUtil.bytesToInt(buf, bindex);
      bindex += 4;
      bindex += Monitor.bytes2nh(str2, buf, bindex);
      bindex += Monitor.bytes2ipprefix(str3, buf, bindex, usenhi);

      String ws1 = StringManip.ws(14 - str1.length());
      String ws2 = StringManip.ws(10 - ("" + asnum).length());
      String ws3 = StringManip.ws(8 - str2.length());
      str += "ID=" + str1 + ws1 + "AS#=" + asnum + ws2 +
             "ASNHI=" + str2 + ws3 + "ASprefix=" + str3 + " " + "\n";
      break;
    case Monitor.NB_INFO:
      caseno = (int)buf[bindex++];
      bindex += Monitor.bytes2ipprefix(str1, buf, bindex, usenhi);
      s = "";
      switch (caseno) {
      case 0:  s = "external";  break;
      case 1:  s = "internal";  break;
      }
      str += "neighbor " + str1 + " (" + s + ")\n";
      break;
    case Monitor.DUMP_FWD_TABLES:
    case Monitor.FWD_TABLES:
      bindex += RadixTreeRoutingTable.bytes2str(str1,buf,bindex,"  | ",usenhi);
      str += "Local Forwarding Table:\n" + str1;
      break;
    case Monitor.JITTER:
      caseno = (int)buf[bindex++];
      d = Double.longBitsToDouble(BytesUtil.bytesToLong(buf,bindex));
      bindex += 8;
      switch (caseno) {
      case 0:
 	str += "Keep Alive Interval jitter factor = " + d + "\n";
 	break;
      case 1:
 	str += "Minimum AS Origination Interval jitter factor = " + d + "\n";
 	break;
      case 2:
 	str += "Minimum Route Advertisement Interval jitter factor = " +d+"\n";
 	break;
      }
      break;
    case Monitor.AGGREG:
      caseno = (int)buf[bindex++];
      switch (caseno) {
      case 0:
 	str += "starting route aggregation process\n";
 	break;
      case 1:
        bindex += Monitor.bytes2ipprefix(str1, buf, bindex, usenhi);
        bindex += Monitor.bytes2ipprefix(str2, buf, bindex, usenhi);
 	str += "aggregating " + str1 + " into " + str2 + "\n";
 	break;
      }
      break;
    case Monitor.NO_MSG_WAITING:
      str += "no msgs waiting to be sent\n";
      break;
    case Monitor.CONN_ESTAB:
      bindex += Monitor.bytes2nh(str1, buf, bindex);
      str += "peering session established with bgp@" + str1 + "\n";

      break;
    case Monitor.STATE_CHANGE:
      i  = (int)buf[bindex++];
      i2 = (int)buf[bindex++];
      bindex += Monitor.bytes2nh(str1, buf, bindex);
      str += "state change: bgp@" + str1 + " " +
 	     BGPSession.statestr[i] + "->" + BGPSession.statestr[i2] + "\n";
      break;
    case Monitor.TIMER_CONFIG:
      caseno = (int)buf[bindex++];
      d = Double.longBitsToDouble(BytesUtil.bytesToLong(buf,bindex));
      bindex += 8;
      switch (caseno) {
      case 0:
 	str += "ConnectRetry Interval configured to " + d + "\n";
 	break;
      case 1:
 	str += "Minimum AS Origination Interval configured to " + d + "\n";
 	break;
      case 2:
        bindex += Monitor.bytes2nh(str1, buf, bindex);
        str += "Hold Timer Interval configured to " + d +
               " for peer bgp@" + str1 + "\n";
 	break;
      case 3:
        bindex += Monitor.bytes2nh(str1, buf, bindex);
        str += "Keep Alive Timer Interval configured to " + d +
               " for peer bgp@" + str1 + "\n";
 	break;
      case 4:
        bindex += Monitor.bytes2nh(str1, buf, bindex);
        str += "Minimum Route Advertisement Interval configured to " + d +
               " for peer bgp@" + str1 + "\n";
 	break;
      }
      break;
    case Monitor.FWD_TABLE_ADD:
      newColor = new Color(0,128,0);
      caseno = (int)buf[bindex++];
      if (caseno == 0) {
        bindex += Monitor.bytes2ipprefix(str1, buf, bindex, usenhi);
        bindex += Monitor.bytes2ipprefix(str2, buf, bindex, usenhi);
        bindex += Monitor.bytes2aspath(str3, buf, bindex, usenhi);
        str += "adding rte=" + str1 + ",nxt=" + str2 + ",asp=" +
               str3 + " to fwd table\n";
      } else if (caseno == 1) {
        bindex += Monitor.bytes2ipprefix(str1, buf, bindex, usenhi);
        bindex += Monitor.bytes2ipprefix(str2, buf, bindex, usenhi);
        str += "adding rte=" + str1 + ",nxt=" + str2 + " to fwd table\n";
      } else {
        Debug.gerr("unexpected case in FWD_TABLE_ADD: " + caseno);
      }
      break;
    case Monitor.FWD_TABLE_RMV:
      newColor = Color.black;
      bindex += Monitor.bytes2ipprefix(str1, buf, bindex, usenhi);
      bindex += Monitor.bytes2ipprefix(str2, buf, bindex, usenhi);
      bindex += Monitor.bytes2aspath(str3, buf, bindex, usenhi);
      str += "removing rte=" + str1 + ",nxt=" + str2 + ",asp=" +
             str3 + " from fwd table\n";
      break;
    case Monitor.SOCKET_EVENT:
      caseno = (int)buf[bindex++];
      switch (caseno) {
      case 1:
        str += "listening for peers on a socket\n";
        break;
      case 2:
        bindex += Monitor.bytes2nh(str1, buf, bindex);
        str += "passively established socket connection with bgp@" +
               str1 + "\n";
        break;
      case 3:
        bindex += Monitor.bytes2nh(str1, buf, bindex);
        str += "writing to socket for peer bgp@" + str1 + "\n";
        break;
      case 4:
        bindex += Monitor.bytes2nh(str1, buf, bindex);
        str += "attempting socket connection with bgp@" + str1 + "\n";
        break;
      case 5:
        bindex += Monitor.bytes2nh(str1, buf, bindex);
        str += "rcv msg on socket connection from bgp@" + str1 + "\n";
        break;
      case 6:
        bindex += Monitor.bytes2nh(str1, buf, bindex);
        str += "closing socket connection with bgp@" + str1 + "\n";
        break;
      case 7:
        bindex += Monitor.bytes2nh(str1, buf, bindex);
        str += "actively established socket connection with bgp@" +str1+ "\n";
        break;
      }
      break;
    case Monitor.REFLECT:
      subtyp = (int)buf[bindex++];
      bindex += Monitor.bytes2nh(str1, buf, bindex);
      bindex += Monitor.bytes2nh(str2, buf, bindex);
      bindex += Monitor.bytes2ipprefix(str3, buf, bindex, usenhi);
      if (subtyp == PeerEntry.CLIENT_FLAG) {
        str += "reflecting from client bgp@" + str1 +
               " to bgp@" + str2 + "  rte=" + str3 + "\n";
      } else {
        str += "reflecting from non-client bgp@" + str1 +
               " to bgp@" + str2 + "  rte=" + str3 + "\n";
      }
      break;
    case Monitor.IBGP_CLUSTER:
      i = (int)buf[bindex++];
      int count = (int)buf[bindex++];
      str += "reflector in cluster " + i + " with client bgp(s)@";
      for (int k=0; k<count; k++) {
        bindex += Monitor.bytes2nh(str1, buf, bindex);
        str += " " + str1;
        str1 = new StringBuffer("");
      }
      str += "\n";
      break;
    case Monitor.HOLD_VALUE:
      d = Double.longBitsToDouble(BytesUtil.bytesToLong(buf,bindex));
      bindex += 8;
      bindex += Monitor.bytes2nh(str1, buf, bindex);
      str += "negotiated Hold Timer Interval of " + d + "s with bgp@" +
             str1 + "\n";
      break;
    case Monitor.KA_VALUE:
      d = Double.longBitsToDouble(BytesUtil.bytesToLong(buf,bindex));
      bindex += 8;
      bindex += Monitor.bytes2nh(str1, buf, bindex);
      str += "calculated Keep Alive Timer Interval of " + d + "s with bgp@" +
             str1 + "\n";
      break;
    case Monitor.IN_POLICY:
      permissible = (buf[bindex++] == 1);
      bindex += Monitor.bytes2ipprefix(str1, buf, bindex, usenhi);
      bindex += Monitor.bytes2aspath(str2, buf, bindex, usenhi);
      bindex += Monitor.bytes2nh(str3, buf, bindex);
      str += "inbound policy " + (permissible?"permitted":"denied") +
             " rte=" + str1 + ",asp=" + str2 + " from bgp@" + str3 +"\n";
      break;
    case Monitor.OUT_POLICY:
      caseno = (int)buf[bindex++];
      bindex += Monitor.bytes2ipprefix(str1, buf, bindex, usenhi);
      bindex += Monitor.bytes2nh(str2, buf, bindex);
      bindex += Monitor.bytes2nh(str3, buf, bindex);
      switch (caseno) {
      case 0:
        str += "outbound policy denied route to " + str1 +
               " for bgp@" + str2 + " (from bgp@" + str3 + ")\n";
        break;
      case 1:
        str += "outbound policy denied withdrawal of " + str1 +
               " for bgp@" + str2 + " (from bgp@" + str3 + ")\n";
        break;
      }
      break;
    case Monitor.DUMP_RIBS_IN:
    case Monitor.RIBS_IN:
      caseno = (int)buf[bindex++];
      StringBuffer str4;
      StringBuffer str5;
      StringBuffer str6 = new StringBuffer("");

      switch (caseno) {
      case 0:
        bindex += Monitor.bytes2nh(str3, buf, bindex);
        break;
      case 1:
        bindex += Monitor.bytes2ipprefix(str1, buf, bindex, usenhi);
        bindex += Monitor.bytes2aspath(str2, buf, bindex, usenhi);
        bindex += Monitor.bytes2nh(str3, buf, bindex);
        str += "adding rte=" + str1 + ",asp=" + str2 +
               " to Adj-RIB-In for bgp@" + str3 + "\n" + Debug.hdr(nh,time);
        break;
      case 2:
        bindex += Monitor.bytes2ipprefix(str1, buf, bindex, usenhi);
        bindex += Monitor.bytes2aspath(str2, buf, bindex, usenhi);
        bindex += Monitor.bytes2nh(str3, buf, bindex);
        str += "removing rte=" + str1 + ",asp=" + str2 +
              " from Adj-RIB-In for bgp@" + str3 + "\n" + Debug.hdr(nh,time);
        break;
      case 3:
        str4 = new StringBuffer("");
        str5 = new StringBuffer("");
        bindex += Monitor.bytes2ipprefix(str1, buf, bindex, usenhi);
        bindex += Monitor.bytes2aspath(str2, buf, bindex, usenhi);
        bindex += Monitor.bytes2nh(str3, buf, bindex);
        bindex += Monitor.bytes2ipprefix(str4, buf, bindex, usenhi);
        bindex += Monitor.bytes2aspath(str5, buf, bindex, usenhi);
        str += "adding rte=" + str1 + ",asp=" + str2 +
               " to Adj-RIB-In for bgp@" + str3 + " (replaces " +
               str4 + ",asp=" + str5 + ")\n" + Debug.hdr(nh,time);
        break;
      }
      bindex += AdjRIBIn.bytes2str(str6, buf, bindex, "  | ", usenhi);
      str += "Adj-RIB-In for bgp@" + str3 + ":\n" + str6;
      break;
    case Monitor.DUMP_LOC_RIB:
    case Monitor.LOC_RIB:
      bindex += LocRIB.bytes2str(str1, buf, bindex, "  | ", usenhi);
      str += "Loc-RIB:\n" + str1;
      break;
    case Monitor.DUMP_RIBS_OUT:
    case Monitor.RIBS_OUT:
      bindex += Monitor.bytes2nh(str1, buf, bindex);
      bindex += AdjRIBOut.bytes2str(str2, buf, bindex, "  | ", usenhi);
      str += "Adj-RIB-Out for bgp@" + str1 + ":\n" + str2;
      break;
    case Monitor.DUMP_STABILITY:
      int sentups = (int)buf[bindex++];
      int rcvdups = (int)buf[bindex++];
      int outups  = (int)buf[bindex++];

      str += "total sent updates:   " + sentups + "\n" + Debug.hdr(nh,time) +
             "total rcvd updates:   " + rcvdups + "\n" + Debug.hdr(nh,time) +
             "outstanding updates:  " + outups  + "\n" + Debug.hdr(nh,time) +
             "final state appears:  " + (outups==0?"":"un") + "stable\n";
      break;      
    default:
      str += "unrecognized monitoring message type: " + typ + "\n";
    }

    AttributedString ret = new AttributedString(str.trim());
    ret.addAttribute(TextAttribute.FOREGROUND, newColor);
    return ret;
  }

    //----------------------------------------------------------------------
    // OPTIONS 

    boolean[] options;
    String nhiFilter;

    private JComponent controls;

    public synchronized Component getControls() {
	if (null==controls) 
	    controls = new JScrollPane(new bgpStreamPlayerControls());
	return controls;
    }

    private class bgpStreamPlayerControls extends JPanel {
	public bgpStreamPlayerControls() {
	    setForeground(Color.black);
	    setBackground(Color.white);
	    options = new boolean[Monitor.bool_opt_names.length];
	    for (int n=0; n<options.length; n++) options[n]=false;
	    for (Stream.Iterator i = stream.iterator();
		 i.hasMoreRecords();) {
		// Make one pass to identify options that 
		// appear in this stream.
		Stream.Record r = i.nextRecord();
		
		if (stream.getRecordTypeString(r.type_code).
		    equals("SSF.OS.BGP4")) 
		    options[r.bytes[r.offset]]=true;
	    }
	    JPanel boxPanel = new JPanel();
	    boxPanel.setLayout
		(new GridLayout(Monitor.bool_opt_names.length/2+1,2));
	    for (int n=0; n<Monitor.bool_opt_names.length; n++) 
		if (!Monitor.bool_opt_names[n].equals("placeholder")){
		    final int m = n;
		    JCheckBox box = new JCheckBox(Monitor.bool_opt_names[n],
						  options[n]);
		    box.addChangeListener(new ChangeListener() {
			    public void stateChanged(ChangeEvent e) {
				options[m] = !options[m];
				stream.redraw();
			    }
			});
		    boxPanel.add(box);
		}

	    JPanel nhiPanel = new JPanel();
	    JTextField nhi = new JTextField(nhiFilter = "-none-");
	    nhiPanel.add(new JLabel("Filter all but NHI prefix:"));
	    nhiPanel.add(nhi);
	    nhi.addActionListener(new ActionListener() {
		    public void actionPerformed(ActionEvent e) {
			nhiFilter = e.getActionCommand();
			stream.redraw();
		    }
		});
	    

	    setLayout(new BorderLayout());
	    add(nhiPanel,BorderLayout.NORTH);
	    add(boxPanel,BorderLayout.CENTER);
	}
    }
}
